/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.jfonia.notation;

import org.jfonia.connect5.basics.MutableValueNode;
import org.jfonia.connect5.basics.Observer;
import org.jfonia.connect5.intervals.BasicIntInterval;
import org.jfonia.model.ISequenceObserver;
import org.jfonia.model.Sequence;
import org.jfonia.model.elements.Clef;
import org.jfonia.model.elements.MusicalElement;
import org.jfonia.model.elements.Tone;
import org.jfonia.pitch.Base40;
import org.jfonia.pitch.RankNodes;

/**
 *
 * @author Rik Bauwens
 */
public class ObservableNote extends ObservableNoteOrRest
{
        private Note note;
        private RankNodes rankNodes;
        private Sequence<Clef> clefSequence;
        private ISequenceObserver clefObserver;

        public ObservableNote(StaffElementSequence parentSequence,
                              Tone tone, BasicIntInterval noteLength)
        {
                super(parentSequence, tone, noteLength);
                requestLock();
                //Initialise nodes
                rankNodes = new RankNodes(tone.getBase40RankNode());
                note = MusicalTime.toNote(tone.getDifference());
                //Initialise observers
                init();
                removeLock();
        }

        private void init()
        {
                getDifferenceNode().addObserver(new Observer()
                {
                        public void onNotify(Object source)
                        {
                                if(!requestLock())
                                        return;
                                setNote(MusicalTime.toNote(((MutableValueNode<Integer>) source).getValue()));
                                removeLock();
                        }
                });
                note.getNoteHeadNode().addObserver(new Observer()
                {
                        public void onNotify(Object source)
                        {
                                setMusicalTime(note);
                        }
                });
                note.getStemNode().addObserver(new Observer()
                {
                        public void onNotify(Object source)
                        {
                                setMusicalTime(note);
                        }
                });
                note.getFlagsNode().addObserver(new Observer()
                {
                        public void onNotify(Object source)
                        {
                                setMusicalTime(note);
                        }
                });
                note.getDotsNode().addObserver(new Observer()
                {
                        public void onNotify(Object source)
                        {
                                setMusicalTime(note);
                        }
                });
        }

        private void setNote(Note note)
        {
                if(note == null)
                        return;
                this.note.setNoteHead(note.getNoteHead());
                this.note.setStem(note.getStem());
                this.note.setFlags(note.getFlags());
                this.note.setDots(note.getDots());
        }

        public ObservableNote setClefSequence(Sequence<Clef> clefSequence)
        {
                if(clefSequence == null)
                        return this;
                if(clefObserver != null)
                        this.clefSequence.removeObserver(clefObserver);
                clefObserver = new ISequenceObserver()
                {
                        public void nodeAdded(Sequence sequence, MusicalElement musicalElement)
                        {
                                setClef((Clef) musicalElement);
                        }

                        public void nodeDifferenceChanged(Sequence sequence, MusicalElement musicalElement)
                        {
                                setClef((Clef) musicalElement);
                        }

                        public void nodeRemoved(Sequence sequence, MusicalElement musicalElement) {}
                        
                };
                clefSequence.addObserver(clefObserver);
                this.clefSequence = clefSequence;
                if(clefSequence.size() > 0)
                        setClef((Clef) clefSequence.getMusicalElement(getBegin()));
                return this;
        }

        private void setClef(Clef clef)
        {
                if((getBegin() >= clef.getBegin() && getBegin() <= clef.getEnd()))
                        rankNodes.setCentralCRankNode(clef.getCentralCRankNode());
        }

        public Note getNote()
        {
                return note;
        }

        public MutableValueNode<Integer> getNoteHeadNode()
        {
                return note.getNoteHeadNode();
        }

        public MutableValueNode<Integer> getStemNode()
        {
                return note.getStemNode();
        }

        public MutableValueNode<Integer> getFlagsNode()
        {
                return note.getFlagsNode();
        }

        public MutableValueNode<Integer> getDotsNode()
        {
                return note.getDotsNode();
        }

        public int getBase40Rank()
        {
                return tone.getBase40Rank();
        }

        public int getRank()
        {
                return Base40.toMusicalInterval(tone.getBase40Rank()).getRank();
        }

        public int getStaffRank()
        {
                return rankNodes.getRankNode().getValue();
        }

        public MutableValueNode<Integer> getRankNode()
        {
                return rankNodes.getRankNode();
        }

        public MutableValueNode<Integer> getAccidentalNode()
        {
                return rankNodes.getAccidentalNode();
        }

        @Override
        public MusicalElement clone()
        {
                BasicIntInterval interval = new BasicIntInterval();
                interval.setBegin(noteLength.getBegin());
                interval.setDifference(noteLength.getDifference());
                ObservableNote clonedNote = new ObservableNote(parentSequence, tone.clone(), interval);
                clonedNote.setClefSequence(clefSequence);
                return clonedNote;
        }
}
